#pragma once
#ifndef __GLF_CORE_ZIP_READER_H_INCLUDED__
#define __GLF_CORE_ZIP_READER_H_INCLUDED__

#include <string>
#include <map>
#include <vector>

#define ZIP_READER_STRING std::string
#define ZIP_READER_MAP std::map

#define USE_ZIPREADER_HASH_KEY 0

#if USE_ZIPREADER_HASH_KEY	
	extern size_t GameEngineCB_HashString( const char* value );
#endif

namespace glf
{
	class IOStream;

namespace core
{
	const signed short ZIP_INFO_IN_DATA_DESCRITOR =	0x0008;

	struct StringComp 
	{
	  bool operator() (const ZIP_READER_STRING& lhs, const ZIP_READER_STRING& rhs) const
	  {
		  return lhs<rhs;
	  }
	};

	template<typename T, unsigned int SIZE>
	struct SSwap
	{
		static T swap(const T& val)
		{
			T tmp;
			unsigned char* dest = (unsigned char*)&tmp;
			unsigned char* src = (unsigned char*)&val;
			for(int i = 0; i < SIZE; ++i){
				dest[i] = src[(SIZE - 1) - i];
			}
			return tmp;
		}
	};

	template<typename T>
	T byteswap(const T& v)
	{
		return SSwap<T, sizeof(T)>::swap(v);
	}

	#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__) 
	#	pragma pack( push, packing )
	#	pragma pack( 1 )
	#	define PACK_STRUCT
	#	pragma warning(disable:4103)
	#elif defined( __GNUC__ ) || defined(SN_TARGET_PSP2)
	#	define PACK_STRUCT	__attribute__((packed))
	#elif defined(__MWERKS__)
	#	pragma pack(1)
	#	define PACK_STRUCT
	#elif defined(_NN_CTR)
	#	pragma pack(1)
	#	define PACK_STRUCT
	#else
	#	error compiler not supported
	#endif

	struct SZIPFileDataDescriptor
	{
		signed int CRC32;
		signed int CompressedSize;
		signed int UncompressedSize;
	} PACK_STRUCT;

	struct SZIPFileHeader
	{
		signed int Sig;
		signed short VersionToExtract;
		signed short GeneralBitFlag;
		signed short CompressionMethod;
		signed short LastModFileTime;
		signed short LastModFileDate;
		SZIPFileDataDescriptor DataDescriptor;
		signed short FilenameLength;
		signed short ExtraFieldLength;
	} PACK_STRUCT;

	#if defined(_MSC_VER) ||  defined(__BORLANDC__) || defined (__BCPLUSPLUS__) 
	#	pragma pack( pop, packing )
	#elif defined(__MWERKS__) 
	#	pragma pack(0)
	#endif

	#undef PACK_STRUCT

	struct SZipFileEntry
	{
		ZIP_READER_STRING zipFileName;
		ZIP_READER_STRING simpleFileName;
		ZIP_READER_STRING path;
		signed int fileDataPosition; // position of compressed data in file
		SZIPFileHeader header;

		bool operator < (const SZipFileEntry& other) const
		{
			for(size_t i=0; i<simpleFileName.length() && i < other.simpleFileName.length(); ++i)
			{
				signed int diff = simpleFileName[i] - other.simpleFileName[i];
				if ( diff )
					return diff < 0;
			}
			return simpleFileName.length() < other.simpleFileName.length();
		}

		bool operator == (const SZipFileEntry& other) const
		{
			return simpleFileName.compare(other.simpleFileName) == 0;
		}
	};

	/*
		Zip file Reader written April 2002 by N.Gebhardt.
		Doesn't decompress data, only reads the file and is able to
		open uncompressed entries.
		Modified by A. Belanger June 2010
	*/
	class CZipReader
	{
	public:
		typedef glf::IOStream DataStream;
		typedef std::vector<ZIP_READER_STRING>	FileListing;
	public:
		CZipReader(DataStream* inFile, const char* filename, bool ignoreCase, bool ignorePaths, bool scanHeader);

		virtual ~CZipReader();

		// opens a file by file name
		virtual bool getFileInfo(const char* filename, signed int &baseOffset, signed int &fileSize);

		// returns the name of the zip file archive
		const char* getZipFileName() const
		{
			return m_archiveName.c_str();
		}

		signed int getFileCount();

		bool isValid(){return File != 0;};

		//
		//zip format "header" is just retarded.  It needs to scan the whole fucking file to have filename and offset
		//which of course does a shit load of seek / read which is exactly what we want to avoid on psp2 since it's 
		//dead slow.
		enum { MAX_ZIP_FILENAME = 1024 };
		void ExportHeader(DataStream& outStream);
		void ImportHeader(DataStream& inStream);
		
		int GetFilesInDirectory( const char* dir, FileListing& outFiles) const;

	private:
			
		//! scans for a local header, returns false if there is no more
		//! local file header.
		bool scanLocalHeader();
		DataStream*			File;
		ZIP_READER_STRING		m_archiveName;

	protected:

		//! splits filename from zip file into useful filenames and paths
		void extractFilename(SZipFileEntry* entry);

		//! deletes the path from a filename
		void deletePathFromFilename(ZIP_READER_STRING& filename);


		bool IgnoreCase;
		bool IgnorePaths;

		#if USE_ZIPREADER_HASH_KEY
			typedef ZIP_READER_MAP<size_t, SZipFileEntry> FileListMap;
			size_t GetFileMapKey( const ZIP_READER_STRING& filename )
			{
				return GameEngineCB_HashString( filename.c_str() );
			}
		#else
			typedef ZIP_READER_MAP<ZIP_READER_STRING, SZipFileEntry, core::StringComp > FileListMap;			
			const ZIP_READER_STRING& GetFileMapKey( const ZIP_READER_STRING& filename )
			{
				return filename;
			}
		#endif
		
		FileListMap FileList;
	};

} //core  

} //glf

#endif //_VOX_ZIP_READER_H_
